package de.uni_saarland.coli.doclists.index.filesystem;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import de.uni_saarland.coli.doclists.index.IDiscIO;

/**
 * 
 * @author stefan
 * 
 *         A list on disc space.
 * 
 *         Only add and get implemented TODO: modify
 */
public class DiscList implements IDiscIO {
	/**
	 * Stores entry infos
	 */
	private RandomAccessFile indexFile;
	/**
	 * Stores entries
	 */
	private RandomAccessFile dataFile;

	/**
	 * file size
	 */
	private long indexFileLength;
	/**
	 * file size
	 */
	private long dataFileLength;

	/**
	 * the current entries bytes
	 */
	private byte[] indexEntryBytes;

	/**
	 * Constructor.
	 * 
	 * opens need to be called before using.
	 */
	public DiscList() {
		this.indexFile = null;
		this.dataFile = null;

		this.indexFileLength = 0L;
		this.dataFileLength = 0L;
		this.indexEntryBytes = new byte[12];
		this.indexFileLength = 0;
		this.dataFileLength = 0;
	}

	/**
	 * Retruns the number of entries in the list.
	 * 
	 * @return the number of entries.
	 */
	public long indexSize() {
		return this.indexFileLength / 12;
	}

	/**
	 * Returns the bytes of the i-th element.
	 * 
	 * @param i
	 *            the index of the element
	 * @return the bytes if idxs < list size otherwise null.
	 */
	public byte[] getBytes(int i) {
		//System.out.println("read "  + i);
		if (i < 0 || this.indexFileLength / 12 <= i)
			return null;

		try {
//			System.out.println(i + " at " + i*12);
			this.indexFile.seek(i * 12);

			this.indexFile.read(this.indexEntryBytes);
			FileIndexEntry entry = new FileIndexEntry(this.indexEntryBytes);
//			System.out.println(entry.getStart());
			byte[] data = new byte[entry.getLength()];

			this.dataFile.seek(entry.getStart());
			this.dataFile.read(data);

			return data;
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Adds an byte array to the disc at the end of the file
	 * 
	 * @param valueBytes
	 *            the bytes
	 * @return true if successfull otherwise false
	 */
	public boolean addBytes(byte[] valueBytes) {
		int len = valueBytes.length;
		FileIndexEntry entry = new FileIndexEntry(this.dataFileLength, len);

		try {
			System.out.println("add entry: " + entry.getStart() + " " + entry.getLength());
			this.indexFile.write(entry.toBytes());
			this.dataFile.write(valueBytes);
			this.indexFileLength += 12;
			this.dataFileLength += len;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

	/**
	 * Adds an byte array to the disc at the end of the file
	 * 
	 * @param valueBytes
	 *            the bytes
	 * @return true if successfull otherwise false
	 */
	public boolean updateData(int idx, byte[] data) {
	//	System.out.println("update " + idx);
		if (idx < 0 || this.indexFileLength / 12 <= idx)
			return false;

		try {
			this.indexFile.seek(idx * 12);

			this.indexFile.read(this.indexEntryBytes);
			FileIndexEntry entry = new FileIndexEntry(this.indexEntryBytes);

			if (entry.getLength() != data.length)
				return false;
						
			this.dataFile.seek(entry.getStart());
			this.dataFile.write(data);
			
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	
	/**
	 * Closes the file.
	 * 
	 * @param file
	 *            the file.
	 */
	private void closeFile(RandomAccessFile file) {
		try {
			if (file != null)
				file.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void save(String path, String baseName) {
		this.closeFile(this.indexFile);
		this.closeFile(this.dataFile);
	}

	@Override
	public void open(String path, String baseName) {
		try {
			this.indexFile = new RandomAccessFile(path + "/" + baseName
					+ ".idx", "rws");
			this.indexFileLength = this.indexFile.length();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			this.dataFile = new RandomAccessFile(path + "/" + baseName
					+ ".data", "rws");
			this.dataFileLength = this.dataFile.length();
		} catch (FileNotFoundException e) {
			this.closeFile(this.indexFile);
			this.indexFileLength = 0;
			e.printStackTrace();
		} catch (IOException e) {
			this.closeFile(this.indexFile);
			this.indexFileLength = 0;
			this.dataFileLength = 0;
			e.printStackTrace();
		}
	}

}
